//
// RS Game Framework
// Copyright © 2009 Jedd Haberstro
// jhaberstro@gmail.com
// 
// $Id:
//

#include <rs/Semaphore.hpp>
#include <rs/Thread.hpp>
#include <rs/unittest/UnitTest.hpp>
#include <vector>

namespace
{
    using namespace rs;
    using namespace rs::unittest;
        
    struct SemaphoreWaitData
    {
        Semaphore* semaphore;
        Boolean flag;
    };
    
    void WaitOnSemaphoreFunc(void* context) {
        SemaphoreWaitData& data = *static_cast< SemaphoreWaitData* >(context);
        data.semaphore->Decrement();
        data.flag = true;
    }
        
    RS_TEST_CASE(rs::Semaphore, NoWaitDecrement)
    {
        Semaphore semaphore(1);
        Thread thread(WaitOnSemaphoreFunc);
        SemaphoreWaitData data = { &semaphore, false };
        
        thread.Start(static_cast< void* >(&data));
        thread.Join();
        
        RS_ASSERT(data.flag == true, "data's flag should not be false");
    }
    
    RS_TEST_CASE(rs::Semaphore, WaitDecrement)
    {
        Semaphore semaphore(0);
        Thread thread(WaitOnSemaphoreFunc);
        SemaphoreWaitData data = { &semaphore, false };
        
        thread.Start(static_cast< void* >(&data));
        Thread::Yield();
        
        RS_ASSERT(data.flag == false, "data.flag should not be false");
        
        semaphore.Increment();
        thread.Join();
        
        RS_ASSERT(data.flag == true, "data's flag should not be false");
    }
    
    
    
    struct MultipleThreadSignalData
    {
        Semaphore* semaphore;
        Boolean flag;
    };
    
    void MultipleThreadSignalFunc(void* context) {
        MultipleThreadSignalData& data = *static_cast< MultipleThreadSignalData* >(context);
        data.semaphore->Decrement();
        data.flag = true;
        data.semaphore->Increment();
    }
    
    RS_TEST_CASE(rs::Semaphore, MultipleThreadSignal)
    {
        enum { kNumThreads = 5 };
        
        MultipleThreadSignalData data[kNumThreads];
        Thread threads[kNumThreads];
        Semaphore semaphore(0);
        
        for (int i = 0; i < kNumThreads; ++i) {
            data[i].semaphore = &semaphore;
            data[i].flag = false;
        }
        
        for (int i = 0; i < kNumThreads; ++i) {
            threads[i] = Thread(MultipleThreadSignalFunc);
            threads[i].Start(static_cast< void* >(&data[i]));
        }
        
        Thread::Yield();
        
        for (int i = 0; i < kNumThreads; ++i) {
            RS_ASSERTF(data[i].flag == false, "Thread %i set flag without being signaled", i);
        }
        
        semaphore.Increment();
        
        for (int i = 0; i < kNumThreads; ++i) {
            threads[i].Join();
        }
        
        for (int i = 0; i < kNumThreads; ++i) {
            RS_ASSERTF(data[i].flag == true, "Thread %i's flag was not set", i);
        }
    }
}